package org.zjt.chapter1.zero.copy;

import io.netty.buffer.*;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.function.Function;


/**
 * Description:  ByteBuf
 *
 *              比起jdk中的ByteBuffer更加方便。
 *
 *      1、零拷贝
 *          实现对象从程序到直接刷出到网络输出流中。
 *        1.1、直接内存
 *        1.2、堆内存
 *        1.3、jdk 文件的零拷贝
 *
 *     2、对象 ByteBuf 的复用
 *
 *
 *     3、池化技术 + 引用计数法
 *
 *      池化Buffer资源   PooledByteBufAllocator .配置ByteBufUtil的池化后，可以内部的默认配置很多都会实现池化内存。
 *      {@link ByteBufUtil}
 *
 *      <code>
 *
 *              -XX:MaxDirectMemorySize=96M
 *              -Dio.netty.allocator.type=pooled
 *              -Dio.netty.noPreferDirect=true     // 默认是直接内存作为池化内存，设置池化内存为堆内存。
 *
 *      </code>
 *
 *
 *      ByteBufAllocator 池化和引用计数
 *      回收策略：内存的引用计数。    refCnt     release 释放引用
 *
 *
         由于Netty是一个NIO网络框架，因此对于Buffer的使用如果基于直接内存（DirectBuffer）实现的话，将会大大提高I/O操作的效率，
         然而DirectBuffer和HeapBuffer相比之下除了I/O操作效率高之外还有一个天生的缺点，即对于DirectBuffer的申请相比HeapBuffer效率更低，
         因此Netty结合引用计数实现了PolledBuffer，即池化的用法，当引用计数等于0的时候，Netty将Buffer回收致池中，
         在下一次申请Buffer的没某个时刻会被复用。Netty这样做的基本想法是我们花了很大的力气申请了一块内存，不能轻易让他被回收呀，能重复利用当然重复利用咯。
 *
 *
 *     4、拷贝
 *          4.1、slice 潜内存拷贝，操作slice将会修改原NettyByteBuf。同一个内存引用。潜拷贝
 *          4.2、copy  产生一个新的副本。 深拷贝
 *
 *
 *
 * @author juntao.zhang
 * Date: 2018-06-29 下午1:23
 * @see
 */
@Slf4j
public class NettyByteBufTest {



    private Function<ByteBuf,String> byteBuf2Str = byteBuf -> {
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        return new String(bytes);
    };

    private Function<ByteBuf,Long> byteBuf2Long = byteBuf -> byteBuf.readLong();

    /***
     * netty中的内容的输出
     */
    @Test
    public void main() {

//        Unpooled.buffer()
//        new PooledByteBufAllocator()


        log.info("dddddd  "+Unpooled.buffer(256).getClass().toString());


        PooledByteBufAllocator allocator = new PooledByteBufAllocator(true);
        ByteBuf byteBuf = allocator.directBuffer(15);
        byteBuf.writeBytes("使用池化内存".getBytes());
        log.info("使用池化内存  " +byteBuf.getClass().toString()+"  中内容：" + byteBuf2Str.apply(byteBuf));



        ByteBuf byteBuf1 = Unpooled.directBuffer().writeBytes("hello world".getBytes());
        log.info("directBuffer byteBuf1:{}",byteBuf2Str.apply(byteBuf1));


        ByteBuf byteBuf2 = Unpooled.compositeBuffer().writeBytes("hello world".getBytes());
        log.info("CompositeByteBuf byteBuf2:{}",byteBuf2Str.apply(byteBuf2));


        ByteBuf byteBuf3 = Unpooled.buffer().writeBytes("hello world".getBytes());
        log.info("heapBuffer byteBuf3:{}",byteBuf2Str.apply(byteBuf3));


        ByteBuf byteBuf4 = Unpooled.copyLong(23L);
        log.info("创建一个long型的 byteBuf4 :{}",byteBuf2Long.apply(byteBuf4));


        ByteBuf byteBuf5 = Unpooled.wrappedBuffer("hello world".getBytes());
        log.info("UnpooledHeapByteBuf byteBuf5:{}",byteBuf2Str.apply(byteBuf5));
        log.info(byteBuf5.toString());


        byteBuf5.readerIndex(0);
        ByteBuf slice = byteBuf5.slice();
        slice.clear();
        slice.writeBytes("zhang ".getBytes());
        log.info("UnpooledHeapByteBuf byteBuf6:{} slice:{}",byteBuf2Str.apply(byteBuf5),byteBuf2Str.apply(slice));
        log.info(byteBuf5.toString());


        byteBuf5.readerIndex(0);
        ByteBuf copy = byteBuf5.copy();
        copy.clear();
        copy.writeBytes("ssss".getBytes());
        log.info("UnpooledHeapByteBuf byteBuf7:{} copy:{}",byteBuf2Str.apply(byteBuf5),byteBuf2Str.apply(copy));

        // 减少对对象引用，当引用为0，返回为true。
        boolean release = copy.release();
        log.info("release:{}\t 引用数RefCnt:{}\t Direct:{}",release,copy.refCnt(),copy.isDirect());

    }


    /**
     * ByteBuf数据的重写。 对象的复用。
     */
    @Test
    public void reWriteByteBuf() {

        ByteBuf byteBuf5 = Unpooled.directBuffer(1024).writeBytes("hello world".getBytes());
        log.info("UnpooledHeapByteBuf byteBuf5:{}",byteBuf2Str.apply(byteBuf5));
        log.info(byteBuf5.toString());

        /** 数据的重写:ByteBuf的重用 **/
        byteBuf5.resetReaderIndex();
        byteBuf5.resetWriterIndex();
        byteBuf5.writeBytes("hello zhang".getBytes());
        log.info(byteBuf5.toString());
        log.info("UnpooledHeapByteBuf byteBuf5:{}",byteBuf2Str.apply(byteBuf5));
    }


    /**
     * netty ByteBuf 中不能重复读。重复读需要重新设置读取index。
     */
    @Test
    public void reReadByteBuf() {
        ByteBuf byteBuf5 = Unpooled.directBuffer(1024).writeBytes("hello world".getBytes());
        log.info("UnpooledHeapByteBuf byteBuf5:{}",byteBuf2Str.apply(byteBuf5));

        log.info("UnpooledHeapByteBuf byteBuf5:{}",byteBuf2Str.apply(byteBuf5));

        byteBuf5.readerIndex(0);
        log.info("UnpooledHeapByteBuf byteBuf5:{}",byteBuf2Str.apply(byteBuf5));
    }

    /***
     * 文件的零拷贝
     *  通过 FileRegion 实现零拷贝
     * @param srcFileName
     * @param destFileName
     * @throws Exception
     */
    public static void copyFileWithFileChannel(String srcFileName, String destFileName) throws Exception {
        RandomAccessFile srcFile = new RandomAccessFile(srcFileName, "r");
        FileChannel srcFileChannel = srcFile.getChannel();

        RandomAccessFile destFile = new RandomAccessFile(destFileName, "rw");
        FileChannel destFileChannel = destFile.getChannel();

        long position = 0;
        long count = srcFileChannel.size();

        srcFileChannel.transferTo(position, count, destFileChannel);

    }


    /**
     * 将文件拷贝到socket 输出流中
     * @param srcFileName
     * @param socketChannel
     * @throws Exception
     */
    public static void copyFileWithFileChannel(String srcFileName, SocketChannel socketChannel) throws Exception {
        RandomAccessFile srcFile = new RandomAccessFile(srcFileName, "r");
        FileChannel srcFileChannel = srcFile.getChannel();
        long position = 0;
        long count = srcFileChannel.size();
        srcFileChannel.transferTo(position, count, socketChannel);
    }




}
